﻿using System.Collections.Concurrent;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using PmSoft.Entity;
using System.Data;
using PmSoft.PetaPoco.Extend;
using PmSoft.DBContext;
using Microsoft.AspNetCore.Http;

namespace PmSoft.ApplicationContext
{
    [UseDI(ServiceLifetime.Scoped, typeof(IApplicationContext))]
    public class ApplicationContext : IApplicationContext
    {
        private readonly ConcurrentDictionary<string, Func<object>> stateResolvers;
        private static ConcurrentDictionary<string, Type> contextStateProviders;
        private readonly IServiceProvider serviceProvider;

        private string _requestUrl = string.Empty;

        public ApplicationContext(IServiceProvider serviceProvider)
        {
            this.serviceProvider = serviceProvider;
            this.stateResolvers = new ConcurrentDictionary<string, Func<object>>();
        }

        static ApplicationContext()
        {
            contextStateProviders = new ConcurrentDictionary<string, Type>();
        }

        private IUser? _user = null;
        /// <summary>
        /// 当前登录用户信息
        /// </summary>
        public IUser? CurrentUser
        {
            get
            {
                if (_user != null)
                    return _user;

                _user = GetStateResolver<IUser>(nameof(CurrentUser));
                return _user;
            }
        }


        public IHttpContextAccessor HttpContextAccessor { get => GetService<IHttpContextAccessor>(); }

        /// <summary>
        /// 是否已认证
        /// </summary>
        public bool IsAuthenticated
        {
            get { return HttpContextAccessor.IsAuthenticated(); }
        }

        /// <summary>
        /// 是否为调试模式
        /// </summary>
        public bool IsDebugMode { get => GetService<IConfiguration>().GetValue("IsDebugMode", false); }

        /// <summary>
        /// 请求的URL
        /// </summary>
        public string? RequestUrl
        {
            get
            {
                if (string.IsNullOrEmpty(_requestUrl))
                    _requestUrl = HttpContextAccessor.GetRequestUrl();
                return _requestUrl;
            }
        }

        /// <summary>
        /// 客户端IP
        /// </summary>
        public string ClientIp { get => HttpContextAccessor.GetRemoteIpAddress(); }

        /// <summary>
        /// 事务
        /// </summary>
        public IDbContextTransaction Transaction { get => GetService<DefaultDbContext>(); }


        private Func<object> FindResolverForState<T>(string name)
        {
            IApplicationContextStateProvider? matchedProvider = null;
            if (!contextStateProviders.ContainsKey(name))
            {
                var allProviders = HttpContextAccessor.GetServices<IApplicationContextStateProvider>();
                foreach (var item in allProviders)
                {
                    if (item.Name == name)
                    {
                        matchedProvider = item;
                    }
                    contextStateProviders.AddOrUpdate(item.Name, key => { return item.GetType(); }, (key, old) => { return item.GetType(); });
                }
            }
            if (matchedProvider == null && !contextStateProviders.ContainsKey(name))
            {
                return () => default(T);
                //throw new Exception($"Cannot find IApplicationContextStateProvider for name {name}. Please regist at first, ServiceCollection.ConfigureStateProvider<{name}>()");
            }
            if (matchedProvider == null)
            {
                matchedProvider = HttpContextAccessor.GetService(contextStateProviders[name]) as IApplicationContextStateProvider;
            }

            if (matchedProvider == null)
                return () => default(T);

            var resolver = matchedProvider.Get<T>();

            if (resolver == null)
                return () => default(T);

            return () => resolver(this);
        }

        public T? As<T>() where T : class, IApplicationContext
        {
            return this as T;
        }

        public T? GetStateResolver<T>(string name)
        {
            var provider = stateResolvers.GetOrAdd(name, key => FindResolverForState<T>(key));
            if (provider != null)
                return (T)provider();
            else
                return default;
        }

        public void SetStateResolver(string name, object value)
        {
            if (stateResolvers.ContainsKey(name))
                stateResolvers[name] = () => value;
            else
                stateResolvers.TryAdd(name, () => value);
        }

        public TService GetService<TService>()
        {
            var service = serviceProvider.GetService<TService>();
            if (service == null)
                throw new ArgumentNullException(nameof(service));
            return service;
        }
    }
}
